package com.linln.admin.app.service.branch;

import com.linln.admin.app.constants.AppConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 调用操作系统的原生git命令执行。
 */
@Slf4j
public class GitBranchHandler extends BranchHelper.BranchHandler {
    @Override
    public boolean support(String type) {
        return AppConstants.REPO_TYPE_GIT.equals(StringUtils.trim(type));
    }

    @Override
    public Map<String, BranchHelper.CommitLog> branchList(String tmpWorkDir, String url, String account, String pass) throws Exception {
        File tmpProjectDir = fetchCode(tmpWorkDir, url, account, pass, "master");
        if (!tmpProjectDir.isDirectory()) {
            return new HashMap<>();
        }
        StringBuilder msg = new StringBuilder();
        int exitCode = GitBranchHandler.execCmd("git branch -a", tmpProjectDir, msg);
        if (exitCode != 0) {
            log.warn("获取app[" + url + "]分支信息失败：" + msg);
            return new HashMap<>();
        }
        Map<String, BranchHelper.CommitLog> ret = new LinkedHashMap<>();
        for (String ver : msg.toString().split("  ")) {
            if (ver.contains(" -> ")) {
                ver = ver.substring(0, ver.indexOf(" -> "));
            }
            ver = ver.trim();
            if (ver.startsWith("remotes/origin/")) {
                ver = ver.substring("remotes/origin/".length());
            }
            if (ver.startsWith("* ")) {
                ver = ver.substring("* ".length());
            }

            //获取最后一次提交的时间和作者
            execCmd("git checkout " + ver, tmpProjectDir);
            StringBuilder tmp = new StringBuilder();
            exitCode = execCmd("git log -1", tmpProjectDir, tmp);
            String author = "";
            Date date = null;
            if (exitCode == 0) {
                //获取
                String lines = tmp.toString();

                for (String line : lines.split("\n")) {
                    line = line.trim();
                    if (line.toLowerCase().startsWith("author:")) {
                        author = line.substring("author:".length()).trim();
                        if (author.contains(" <")) {
                            author = author.substring(0, author.indexOf(" <"));
                        }
                    } else if (line.toLowerCase().startsWith("date:")) {
                        date = new Date(line.substring("date:".length()).trim());
                    }
                }
            }
            BranchHelper.CommitLog clog = new BranchHelper.CommitLog();
            clog.setUser(author);
            clog.setCommitTime(date);
            ret.put(ver, clog);
        }
        return ret;
    }

    /**
     * 创建一个新分支，指定从哪个分支为基准
     *
     * @param url  主干url或来源url
     * @param orig 源分支
     * @param dest 目标分支
     * @return 成功-true；其它-false
     */
    @Override
    public boolean createNewBranch(String tmpWorkDir, String url, String account, String pass, String orig, String dest) throws Exception {
        File tmpProjectDir = fetchCode(tmpWorkDir, url, account, pass, orig);
        if (!tmpProjectDir.isDirectory()) {
            return false;
        }
        try {
            execCmd("git checkout -b " + dest, tmpProjectDir);
            StringBuilder msg = new StringBuilder();
            int exitCode = execCmd("git push origin " + dest, tmpProjectDir, msg);
            if (exitCode != 0 || msg.toString().toLowerCase().contains("error: failed to push some ")) {
                return false;
            }
        } finally {
            try {
                FileUtils.deleteDirectory(tmpProjectDir);
            } catch (Exception ignore) {
            }
        }
        return true;
    }

    /**
     * 摘取指定分支的代码
     *
     * @param tmpWorkDir 工作目录
     * @param url url
     * @param orig 源头
     * @return 返回结果
     * @throws IOException 异常
     * @throws InterruptedException 异常
     */
    private File fetchCode(String tmpWorkDir, String url, String account, String pass, String orig) throws IOException, InterruptedException {
        log.info("开始拉取代码：" + url);
        String projectDir = FilenameUtils.getName(url);
        if (StringUtils.isBlank(projectDir)) {
            log.warn("git的url不正确：" + url);
            return new File("NOT_EXISTS_DIR/GIT_CHECKOUT");
        }
        if (projectDir.toLowerCase().endsWith(".git")) {
            projectDir = projectDir.substring(0, projectDir.length() - 4);
        }
        String workDir = AppConstants.GIT_WORK_DIR + "/" + tmpWorkDir;
        if (!new File(workDir).isDirectory()) {
            new File(workDir).mkdirs();
        }
        File projectCodeDir = new File(workDir, projectDir);
        if (projectCodeDir.isDirectory()) {
            FileUtils.deleteDirectory(projectCodeDir);
            try {
                projectCodeDir.delete();
            } catch (Exception ignore) {
            }
        }
        //把url拼接上账号和密码
        String tarUrl = "";
        tarUrl += url.substring(0, url.indexOf("://")) + "://";
        url = url.substring(url.indexOf("://") + 3);
        String domain = url.substring(0, url.indexOf("/"));
        if (domain.contains("@")) {
            domain = domain.substring(domain.indexOf("@") + 1);
        }
        tarUrl += account + ":" + pass + "@" + domain + url.substring(url.indexOf("/"));
        StringBuilder msg = new StringBuilder();
//        log.info("git命令：" + "git clone -b " + orig + " " + tarUrl);
        execCmd("git clone -b " + orig + " " + tarUrl, new File(workDir), msg);
        if (msg.toString().toLowerCase().startsWith("fatal: ")) {
            log.warn("出错的命令：" + "git clone -b " + orig + " " + tarUrl);
            return new File("NOT_EXISTS_DIR/GIT_CHECKOUT");
        }
        return projectCodeDir;
    }

    /**
     * 合并两个分支代码
     *
     * @param url  主干url或来源url
     * @param orig 源分支
     * @param dest 目标分支
     * @return 成功-true；有冲突-false
     */
    @Override
    public boolean mergeBranch(String tmpWorkDir, String url, String account, String pass, String orig, String dest) throws Exception {
        String projectDir = FilenameUtils.getBaseName(url);
        File tmpProjectDir = fetchCode(tmpWorkDir, url, account, pass, orig);
        if (!tmpProjectDir.isDirectory()) {
            throw new IllegalArgumentException(projectDir + "项目的分支[" + orig + "]不存在!");//如果目录不存在，则表示分支不存在，就会合并失败
        }
        try {
            execCmd("git pull", tmpProjectDir);

            execCmd("git checkout " + dest, tmpProjectDir);
            execCmd("git pull", tmpProjectDir);
            String cmd = "git merge " + orig;//+ " -m \"自动合并分支 " + orig + "->" + dest + "\"";
            execCmd(cmd, tmpProjectDir);

            StringBuilder msg = new StringBuilder();
            int exitCode = execCmd("git status", tmpProjectDir, msg);
            if (exitCode != 0 || msg.toString().toLowerCase().contains("fix conflicts and run") || msg.toString().contains("解决冲突并运行")) {
                return false;
            }
            msg.delete(0, msg.length());
            exitCode = execCmd("git push origin " + dest, tmpProjectDir, msg);
            String errMsg = msg.toString();
            return exitCode == 0 && !errMsg.toLowerCase().contains("error: failed to push some ") && !errMsg.toLowerCase().startsWith("error:") && !errMsg.toLowerCase().startsWith("错误:");
        } finally {
            try {
                FileUtils.deleteDirectory(tmpProjectDir);
            } catch (Exception ignore) {
            }
        }
    }

    @Override
    public File checkoutBranch(String tmpWorkDir, String url, String account, String pass, String branch) throws Exception {
        File tmpProjectDir = fetchCode(tmpWorkDir, url, account, pass, branch);
        if (!tmpProjectDir.isDirectory()) {
            return null;
        }
        return tmpProjectDir;
    }

    @Override
    public boolean deleteBranch(String tmpWorkDir, String url, String account, String pass, String branch) throws Exception {
        String projectDir = FilenameUtils.getBaseName(url);
        File tmpProjectDir = fetchCode(tmpWorkDir, url, account, pass, "master");
        if (!tmpProjectDir.isDirectory()) {
            throw new IllegalArgumentException(projectDir + "项目的分支[" + branch + "]不存在!");//如果目录不存在，则表示分支不存在，就会合并失败
        }

        StringBuilder msg = new StringBuilder();
        int exitCode = execCmd("git branch -r -d origin/" + branch, tmpProjectDir, msg);
        if (exitCode != 0) {
            log.warn("删除本地app[" + projectDir + "]分支信息失败：" + msg);
            return false;
        }
        exitCode = execCmd("git push origin --delete " + branch, tmpProjectDir, msg);
        if (exitCode != 0) {
            log.warn("删除远程app[" + projectDir + "]分支信息失败：" + msg);
            return false;
        }
        return true;
    }
}
